//-------------------------------------------------------------------------
/**
   File:   flagset.h

   Descr.: A typesafe way to handle a set of flags defined by an enum-type.

   Author: Stefan Leihkauf
   Date:   8/10/2011
*/
//-------------------------------------------------------------------------
#ifndef _FLAGSET_H
#define _FLAGSET_H

template <typename EnumType, typename ValueType = int>
class flagset
{
public:
	typedef typename EnumType enum_t;	// enumeration typename
	typedef typename ValueType value_t;	// typename of the underlying value

public:
	/// constructor
	inline flagset() : _flags(0) {
		/* empty */
	}

	/// constructor
	inline flagset(EnumType t) : _flags(t) {
		/* empty */
	}

	/// constructor
	inline flagset(EnumType t1, EnumType t2) : _flags(t1 | t2) {
		/* empty */
	}

	/// constructor
	inline flagset(EnumType t1, EnumType t2, EnumType t3) : _flags(t1 | t2 | t3) {
		/* empty */
	}

	/// constructor
	inline flagset(EnumType t1, EnumType t2, EnumType t3, EnumType t4) : _flags(t1 | t2 | t3 | t4) {
		/* empty */
	}

	/// set flag (bitwise OR)
	inline void set(EnumType t) {
		_flags |= t;
	}

	/// set flag (bitwise OR)
	inline void set(EnumType t1, EnumType t2) {
		_flags |= t1 | t2;
	}

	/// set flag (bitwise OR)
	inline void set(EnumType t1, EnumType t2, EnumType t3) {
		_flags |= t1 | t2 | t3;
	}

	/// set flag (bitwise OR)
	inline void set(EnumType t1, EnumType t2, EnumType t3, EnumType t4) {
		_flags |= t1 | t2 | t3 | t4;
	}

	/// clear flag (bitwise NAND)
	inline void unset(EnumType t) {
		_flags &= ~t;
	}

	/// test if a given flag is set
	inline bool test(EnumType t) {
		return ((this->_flags & t) != 0);
	}

	/// convert to unsigned long
	inline unsigned long to_ulong() const {
		return (unsigned long)_flags;
	}

	/// convert to unsigne 64 bit
	inline unsigned long long to_ulonglong() const {
		return (unsigned long long)_flags;
	}

	/// equality
	inline bool operator == (const flagset<EnumType, ValueType>& t) const {
		return (this->_flags == t._flags);
	}

	/// inequality
	inline bool operator != (const flagset<EnumType, ValueType>& t) const {
		return !(this->_flags == t._flags);
	}

	/// logical NOT
	inline bool operator ! () const {
		return (this->_flags == 0);
	}

	/// assignment
	inline flagset<EnumType, ValueType>& operator = (const flagset<EnumType, ValueType>& s) {
		if(s != *this)
			this->_flags = s._flags;
		return *this;
	}

	/// bitwise AND assignment
	inline flagset<EnumType, ValueType>& operator &= (const flagset<EnumType, ValueType>& s) {
		_flags &= s._flags;
		return *this;
	}

	/// bitwise inclusive OR assignment
	inline flagset<EnumType, ValueType>& operator |= (const flagset<EnumType, ValueType>& s) {
		_flags |= s._flags;
		return *this;
	}

	/// XOR assignment
	inline flagset<EnumType, ValueType>& operator ^= (const flagset<EnumType, ValueType>& s) {
		_flags ^= s._flags;
		return *this;
	}

	/// one's complement
	inline flagset<EnumType, ValueType> operator ~ () const {
		return flagset<EnumType, ValueType>((enum_t)(~this->_flags));
	}

private:
	value_t _flags;
};
#endif //!defined _FLAGSET_H
